package com.basker.pisces.orm.data.meta;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.CollectionUtils;

import com.basker.pisces.core.meta.ICollectionField;
import com.basker.pisces.core.meta.IDataField;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.IField;
import com.basker.pisces.exception.ExceptionFactory;
import com.basker.pisces.utils.ReflectionUtils;

/**
 * 用来描述单个或多个数据表结构集合：包括主表和子表的信息。
 * <p>
 * 通过{@link DataSetMeta#create(IDataObjectMeta)}创建实例
 */
public class DataSetMeta {

    /**
     * 根据对象元信息创建数据集元信息
     *
     * @param objectMeta
     * @return
     */
    public static DataSetMeta create(IDataObjectMeta objectMeta) {
        DataSetMeta dataSetMeta = new DataSetMeta();

        TableMeta rootTableMeta = dataSetMeta.buildTableMeta(objectMeta, true);
        dataSetMeta.setRootTableMeta(rootTableMeta);

        return dataSetMeta;
    }

    public static FieldMeta createFieldMeta(IDataField dataField) {
        return ReflectionUtils.createNewInstance(FieldMeta.class, new Class<?>[]{IDataField.class}, dataField);
    }

    // 根表、主表
    private TableMeta rootTableMeta;
    private String schema;

    // 这里只需要放主子表，subtable不要放(在查询时，先查主表再查字表，在查主表的时候会将扩展表查出来，所以subtable是跟着DataSetMeta的)
    private Map<String, TableMeta> tableMetas = new LinkedHashMap<>();

    private DataSetMeta() {

    }

    /**
     * 获取根表
     *
     * @return
     */
    public TableMeta getRootTableMeta() {
        return rootTableMeta;
    }

    public String getSchema() {
        return schema;
    }

    /**
     * 获取指定表的指定扩展表
     *
     * @param tableName
     * @param subtableName
     * @return
     */
    public TableMeta getSubTableMeta(String tableName, String subtableName) {
        TableMeta tableMeta = tableMetas.get(tableName);
        return tableMeta != null ? tableMeta.getSubTableMeta(subtableName) : null;
    }

    /**
     * 获取指定表
     *
     * @param tableName
     * @return
     */
    public TableMeta getTableMeta(String tableName) {
        return tableMetas.get(tableName);
    }

    /**
     * 获取所有表,但不包括subtable
     *
     * @return
     */
    public Collection<TableMeta> getTableMetas() {
        return tableMetas.values();
    }

    /**
     * 根据对象元信息，构建对应的表元信息
     *
     * @param objectMeta
     * @param buildChildren
     * @return
     */
    TableMeta buildTableMeta(IDataObjectMeta objectMeta, boolean buildChildren) {
        // 1.对象映射的主表
        TableMeta mainTableMeta = new TableMeta(objectMeta.getTableName());
        this.schema = objectMeta.getSchema();

        this.addTableMeta(mainTableMeta);

        // 2.对象的扩展表
        String[] subTableNames = objectMeta.getSubTableNames();
        if (!ArrayUtils.isEmpty(subTableNames)) {
            for (String subtableName : subTableNames) {
                TableMeta subTableMeta = new TableMeta(subtableName);
                mainTableMeta.addSubTableMeta(subTableMeta);
            }
        }

        Collection<IField> fields = objectMeta.getFields();
        for (IField field : fields) {
            // 3.获取objectMeta的字段，创建TableMeta的fieldMetas
            if (field instanceof IDataField) {
                IDataField dataField = (IDataField) field;
                FieldMeta fieldMeta = createFieldMeta(dataField);

                TableMeta hostTableMeta = this.getHostTableMeta(mainTableMeta, dataField.getHostTableName());
                if (hostTableMeta == null) {
                    throw ExceptionFactory.createRuntimeException("pisces-orm.DataSetMeta.buildTableMeta-001",
                            "dataField:{0} hostTable is invalid!", dataField);
                }

                hostTableMeta.addFieldMeta(fieldMeta);

                if (dataField.isIndexField()) {
                    hostTableMeta.addIndexMeta(createIndexMeta(dataField));
                }

                if (dataField.isPkField()) {
                    this.setFkFieldForSubTable(mainTableMeta, fieldMeta);
                }

                continue;
            }

            // 4.获取objectMeta的集合字段，创建TableMeta的childtable,这是一个递归过程
            if (field instanceof ICollectionField) {
                if (!buildChildren) {
                    continue;
                }

                // 当前是集合字段，获取此集合项（子表）的数据对象
                ICollectionField collectionField = (ICollectionField) field;
                IDataObjectMeta collectionDataObjectMeta = collectionField.getItemObjectMeta();
                // 创建TableMeta的childtable
                TableMeta collectionTableMeta = buildTableMeta(collectionDataObjectMeta, buildChildren);

                FieldMeta fieldMeta = createFieldMeta(objectMeta.getPkField());
                fieldMeta.setComment(collectionField.getFkFieldDescription());

                collectionTableMeta.setFkFieldMeta(fieldMeta);
                // 子表关联主表主键的字段需要创建索引
                collectionTableMeta.addIndexMeta(createIndexMeta(objectMeta.getPkField()));

                mainTableMeta.addChildTableMeta(collectionTableMeta);

                continue;
            }

        }

        return mainTableMeta;
    }

    private IndexMeta createIndexMeta(IDataField dataField) {
        return new IndexMeta("idx_" + dataField.getDbFieldName(), dataField.getDbFieldName());
    }

    private void addTableMeta(TableMeta tableMeta) {
        this.tableMetas.put(tableMeta.getName(), tableMeta);
    }

    private TableMeta getHostTableMeta(TableMeta mainTableMeta, String tableName) {
        if (mainTableMeta.getName().equals(tableName)) {
            return mainTableMeta;
        }

        return mainTableMeta.getSubTableMeta(tableName);
    }

    /**
     * 为一对一扩展表设置外键
     *
     * @param mainTableMeta
     * @param fieldMeta
     */
    private void setFkFieldForSubTable(TableMeta mainTableMeta, FieldMeta fieldMeta) {
        List<TableMeta> subTableMetas = mainTableMeta.getSubTableMetas();
        if (CollectionUtils.isEmpty(subTableMetas)) {
            return;
        }

        IDataField pkField = fieldMeta.getDataField();
        FieldMeta fkfieldMeta = createFieldMeta(pkField);

        for (TableMeta subTableMeta : subTableMetas) {
            // 设置一对一扩展表的外键
            subTableMeta.setFkFieldMeta(fkfieldMeta);

            FieldMeta pkFieldMeta4SubTable = createFieldMeta(pkField);
            pkFieldMeta4SubTable.setTableName(subTableMeta.getName());

            // 每个一对一扩展 表需要设置自身的主键
            subTableMeta.addFieldMeta(pkFieldMeta4SubTable);
        }
    }

    private void setRootTableMeta(TableMeta rootTableMeta) {
        this.rootTableMeta = rootTableMeta;
    }

}
